/*
 ============================================================================
 Name        : PPD.c
 Author      : SOogle
 Version     : 1.0
 Copyright   : All rights reserved
 Description : FUSELAGE - FUSE FAT32 FILE SYSTEM
 ============================================================================
 */


#include "../headers/GlobalHeader.h"

struct_config configuracion;
PPD_CHS posicionActualCabezal, proximoSectorPlanifico, posicionAnterior;
int32_t filesize;
t_Cola *colaPedidos, *colaActiva;
pthread_mutex_t mutexCola, mutexArchivoMapeado;
int32_t SUBIENDO;
int32_t modoInicio, algoritmoUtilizado;
int32_t sizeCola, sizeColaActiva;
extern t_log *logger;
char *handlerArchivoMapeado;
int32_t cantidadClientesPFS = 0;
NodoPFS *listaPFS;

int main(void)
{
	pthread_t thread_lectorPedidos_pfs_id, thread_lectorPedidos_raid_id, thread_lectorPedidos_consola_id, thread_msync_id;
	int32_t addrlen = sizeof(struct sockaddr_in);
	int32_t cliente_ppd = socket(AF_INET,SOCK_STREAM,0);
	int32_t rc;
	nipc paquete_mando, paquete_recibo;
	struct_thread_lector struct_thread_lector;
	t_pedido pedidoPlanificacion;
	int32_t *colaLog;

	//CARGO LA CONFIGURACION INICIAL
	configuracion = cargaConfiguracionInicial();

	//INICIALIZO LAS ESTRUCTURAS
	inicializarEstructuras();

	printf("!!!Hola soy el PPD: %s \n",configuracion.DiscoID);
	printf("\n");

	if (configuracion.LogActivado == 1)
	{
		log_info(logger, "PPD - MAIN", "Se cargo exitosamente el archivo de configuracion\n");
	}

	//CREO LA COLA DE PEDIDOS
	colaPedidos = crearCola();
	if (configuracion.LogActivado == 1)
	{
		log_info(logger, "PPD - MAIN", "Se creo exitosamente la cola de pedidos\n");
	}


	//CREO THREAD DE SINCRONIZACION
	pthread_create(&thread_msync_id,NULL,(void*)&thread_msync,NULL);

	//INICIO SEMAFOROS Y MUTEX
	pthread_mutex_init (&mutexArchivoMapeado, NULL);
	pthread_mutex_init(&mutexCola, NULL);

	//CARGO EL STRUCT DIRECCION LOCAL Y REMOTA
	struct sockaddr_in *direccion_local = malloc(sizeof(struct sockaddr_in));
	struct sockaddr_in *direccion_remota = malloc(sizeof(struct sockaddr_in));

	//CREO THREAD DE LECTOR DE PEDIDOS DE CONSOLA, EL CUAL ES UTILIZADO TANTO POR LISTEN COMO POR CONNECT
	pthread_create(&thread_lectorPedidos_consola_id,NULL,(void *)&thread_lector_consola,NULL);

	switch(modoInicio)
	{
		case CONNECT:

			direccion_local->sin_family = AF_INET;
			direccion_local->sin_addr.s_addr = INADDR_ANY;
			direccion_local->sin_port = htons(configuracion.PuertoEscuchaPpd);

			direccion_remota->sin_family = AF_INET;
			direccion_remota->sin_addr.s_addr = inet_addr(configuracion.IPRaid);
			direccion_remota->sin_port = htons(configuracion.PuertoRaid);

			rc = connect(cliente_ppd,(struct sockaddr *)direccion_remota,addrlen);
			if (rc < 0)
			{
				//LOGUEO EL ERROR DE CONNECT
				if (configuracion.LogActivado==1)
					log_error(logger, "MAIN", "%s: Error al intentar conectarse al RAID a traves del socket %i \n", cliente_ppd);
				close(cliente_ppd);
				cerrarArchivo();
				exit(1);
			}

			if (configuracion.LogActivado==1)
				log_info(logger, "MAIN", "Se conecto el RAID a traves del socket: %i \n", cliente_ppd);

			//PREPARO EL HANDSHAKE
			paquete_mando.type = 0;
			paquete_mando.payloadlength = sizeof(t_pedido);
			strcpy(paquete_mando.payload.planificador_id,configuracion.DiscoID);
			paquete_mando.payload.direccionSector = cantidadTotalDeSectores();

			rc = send(cliente_ppd,&paquete_mando,sizeof(nipc),0);
			if (rc < 0)
			{
				if (configuracion.LogActivado == 1)
					log_error(logger, "MAIN", "Error al enviar Handshake \n");
				cerrarArchivo();
				exit(1);
			}
			if (configuracion.LogActivado == 1)
				log_info(logger, "MAIN", "Se mando handshake \n");

			rc = recv(cliente_ppd,&paquete_recibo,sizeof(nipc),0);
			if (rc < 0)
			{
				if (configuracion.LogActivado == 1)
					log_error(logger, "MAIN", "Error al recibir handshake \n");
				exit (1);
			}

			if(paquete_recibo.payloadlength == 0)
			{
				struct_thread_lector.socket_id = cliente_ppd;
				if (configuracion.LogActivado == 1)
					log_info(logger, "MAIN", "Se recibio el handshake correctamente \n");
			}

			pthread_create(&thread_lectorPedidos_raid_id,NULL,(void*)&thread_lector_pedidos_raid,(void *)&struct_thread_lector);

			switch(algoritmoUtilizado)
			{
				case CON_INANICION:

					while(TRUE)
					{

						pthread_mutex_lock (&mutexCola);
						sem_getvalue(&(colaPedidos->cantidadPedidos), &sizeCola);
						//MIENTRAS HAYA PEDIDOS EN LA COLA, LOS VOY A PROCESAR
						pthread_mutex_unlock (&mutexCola);
						//puts("1");
						if ((sizeCola > 0) && (colaPedidos->frente!=NULL))
						{
							sem_wait(&(colaPedidos->cantidadPedidos));
						//	puts("2");				//PIDO LOS SECTORES DE LA COLA PARA LOGUEARLOS
							//BLOQUEO LA LISTA PARA PODER TRABAJAR
							pthread_mutex_lock (&mutexCola);
							sem_getvalue(&(colaPedidos->cantidadPedidos), &sizeCola);
							//SACO UN PEDIDO DE LA COLA PARA PROCESARLO, ACA LA FUNCION SE LLAMARIA TOMOPEDIDO();
							if(configuracion.LogActivado == 1)
							{
								colaLog = armarColaLogueoSSTF();
							}
							pedidoPlanificacion = quitarPedido_SSTF();
							pthread_mutex_unlock(&mutexCola);
							//UNA VEZ QUE TOME EL PEDIDO, YA PUEDO LIBERAR LA COLA PARA QUE SE AGREGUEN MAS PEDIDOS DE LOS OTROS THREADS
							//TENGO QUE VER EL TIPO DE PEDIDO, SI ES DE CONSOLA O SI ES DE PFS
							switch(pedidoPlanificacion.conexion_pfs_id)
							{
								case PEDIDO_CONSOLA:
									//SI ES DE CONSOLA, PROCESO Y AVISO QUE FUE PROCESADO ASI PUEDO DEVOLVER POR STDOUT
									//DENTRO DE ESTA FUNCION, DEBE ESTAR LA ENCARGADA DE LEER Y ESCRIBIR EN DISCO
									paquete_mando = procesoPedidoElegido(pedidoPlanificacion, colaLog);
									//free(colaLog);
									switch (pedidoPlanificacion.lectura_o_escritura)
									{
										case 'L' : //sos trace
											enviaRespuestaTRACE(posicionActualCabezal, posicionAnterior, pedidoPlanificacion.direccionSector);
											break;

										case 'E' : //sos clean
											enviaRespuestaCLEAN(paquete_mando.type);
											if (configuracion.LogActivado == 1)
												log_info(logger, "MAIN", "Se envio la respuesta del pedido de E sobre el sector %d:%d correctamente \n", posicionActualCabezal.cylinder, posicionActualCabezal.sector);
											break;
									}

									break;

										default:
											//SI ES DE PFS, O SEA, SI ES DISTRIBUIDO POR EL RAID
											//DENTRO DE ESTA FUNCION, DEBE ESTAR LA ENCARGADA DE LEER Y ESCRIBIR EN DISCO
											paquete_mando = procesoPedidoElegido(pedidoPlanificacion, colaLog);
											//free(colaLog);

											send(cliente_ppd,&paquete_mando,sizeof(nipc),0);
											//printf("procese %i de %c ;;",paquete_mando.payload.direccionSector,paquete_mando.payload.lectura_o_escritura);
											if (configuracion.LogActivado == 1)
												log_info(logger, "MAIN", "Se envio la respuesta del pedido de E sobre el sector %d:%d correctamente \n", posicionActualCabezal.cylinder, posicionActualCabezal.sector);
											break;
							}
						}
					}
					break;

				case SIN_INANICION:

					while(TRUE)
					{
						pthread_mutex_lock (&mutexCola);
						sem_getvalue(&(colaPedidos->cantidadPedidos), &sizeCola);
						//MIENTRAS HAYA PEDIDOS EN LA COLA, LOS VOY A PROCESAR
						pthread_mutex_unlock (&mutexCola);
						//puts("1");
						if ((sizeCola > 0) && (colaPedidos->frente!=NULL))
						{
							//CREO UNA COLA PARA TRABAJAR CON LOS N PEDIDOS
							colaActiva = crearCola();

							pthread_mutex_lock (&mutexCola);

							//ARMO LA COLA DE LOGUEO ANTES DE QUITAR LOS PEDIDOS QUE VOY A PROCESAR
							if(configuracion.LogActivado == 1)
							{
								colaLog = armarColaLogueoNSTEP();
							}

							//ARMO LA COLA CON LA QUE VOY A TRABAJAR
							colaActiva = armarColaActiva();

							//ACTUALIZO EL SIZE DE LA COLA
							sem_getvalue(&colaPedidos->cantidadPedidos, &sizeCola);

							pthread_mutex_unlock(&mutexCola);

							//MIENTRAS HAYA PEDIDOS EN LA COLA ACTIVA
							while(colaActiva->frente != NULL)
							{
								if(configuracion.LogActivado == 1)
								{
									colaLog = armarColaLogueoNSTEP();
								}

								pedidoPlanificacion = quitarPedido_NSTEP();

								switch(pedidoPlanificacion.conexion_pfs_id)
								{
									case PEDIDO_CONSOLA:
										paquete_mando = procesoPedidoElegido(pedidoPlanificacion, colaLog);
										//free(colaLogueo);
										switch (pedidoPlanificacion.lectura_o_escritura)
										{
											//ES UN PEDIDO DE TRACE
											case 'L' :
												enviaRespuestaTRACE(posicionActualCabezal, posicionAnterior, pedidoPlanificacion.direccionSector);
												if (configuracion.LogActivado == 1)
													log_info(logger, "MAIN", "Se envio la respuesta del pedido de E sobre el sector %d:%d correctamente \n", posicionActualCabezal.cylinder, posicionActualCabezal.sector);
												break;

												//ES UN PEDIDO DE CLEAN
											case 'E' :
												enviaRespuestaCLEAN(paquete_mando.type);
												if (configuracion.LogActivado == 1)
													log_info(logger, "MAIN", "Se envio la respuesta del pedido de E sobre el sector %d:%d correctamente \n", posicionActualCabezal.cylinder, posicionActualCabezal.sector);
												break;
										}
										break;

										//ES UN PEDIDO DE RAID
											default:
												paquete_mando = procesoPedidoElegido(pedidoPlanificacion, colaLog);
												//free(colaLogueo);
												//LUEGO DE SER PROCESADO, ES DECIR, ACTUALIZADO LA POSICION ACTUAL DEL CABEZAL, SE DEBE BORRAR DE LA LISTA
												send(cliente_ppd,&paquete_mando,sizeof(nipc),0);

												if (configuracion.LogActivado == 1)
													log_info(logger, "MAIN", "Se envio la respuesta del pedido de E sobre el sector %d:%d correctamente \n", posicionActualCabezal.cylinder, posicionActualCabezal.sector);
												break;
								}
							}

							destruirCola(colaActiva);
						}
					}
					break;
			}

			break;

				case LISTEN:

					pthread_create(&thread_lectorPedidos_pfs_id,NULL,(void*)&thread_lector_pedidos_pfs,NULL);

					switch(algoritmoUtilizado)
					{
						case CON_INANICION:

							while(TRUE)
							{
								pthread_mutex_lock (&mutexCola);
								sem_getvalue(&(colaPedidos->cantidadPedidos), &sizeCola);
								//MIENTRAS HAYA PEDIDOS EN LA COLA, LOS VOY A PROCESAR
								pthread_mutex_unlock (&mutexCola);
								//puts("1");
								if ((sizeCola > 0) && (colaPedidos->frente!=NULL))
								{
									sem_wait(&(colaPedidos->cantidadPedidos));
									//PIDO LOS SECTORES DE LA COLA PARA LOGUEARLOS
									//BLOQUEO LA LISTA PARA PODER TRABAJAR
									pthread_mutex_lock (&mutexCola);
									//SACO UN PEDIDO DE LA COLA PARA PROCESARLO, ACA LA FUNCION SE LLAMARIA TOMOPEDIDO();
									if(configuracion.LogActivado == 1)
									{
										colaLog = armarColaLogueoSSTF();
									}
									pedidoPlanificacion = quitarPedido_SSTF();
									pthread_mutex_unlock(&mutexCola);
									//UNA VEZ QUE TOME EL PEDIDO, YA PUEDO LIBERAR LA COLA PARA QUE SE AGREGUEN MAS PEDIDOS DE LOS OTROS THREADS
									//TENGO QUE VER EL TIPO DE PEDIDO, SI ES DE CONSOLA O SI ES DE PFS
									switch(pedidoPlanificacion.conexion_pfs_id)
									{
										case PEDIDO_CONSOLA:
											//SI ES DE CONSOLA, PROCESO Y AVISO QUE FUE PROCESADO ASI PUEDO DEVOLVER POR STDOUT
											//DENTRO DE ESTA FUNCION, DEBE ESTAR LA ENCARGADA DE LEER Y ESCRIBIR EN DISCO
											paquete_mando = procesoPedidoElegido(pedidoPlanificacion, colaLog);
											//free(colaLog);
											switch (pedidoPlanificacion.lectura_o_escritura)
											{
												case 'L' : //sos trace
													enviaRespuestaTRACE(posicionActualCabezal, posicionAnterior, pedidoPlanificacion.direccionSector);
													break;

												case 'E' : //sos clean
													enviaRespuestaCLEAN(paquete_mando.type);
													if (configuracion.LogActivado == 1)
														log_info(logger, "MAIN", "Se envio la respuesta del pedido de E sobre el sector %d:%d correctamente \n", posicionActualCabezal.cylinder, posicionActualCabezal.sector);
													break;
											}

											break;

												default:
													//SI ES DE PFS, O SEA, SI ES DISTRIBUIDO POR EL RAID
													//DENTRO DE ESTA FUNCION, DEBE ESTAR LA ENCARGADA DE LEER Y ESCRIBIR EN DISCO
													paquete_mando = procesoPedidoElegido(pedidoPlanificacion, colaLog);
													//free(colaLog);

													send(pedidoPlanificacion.conexion_pfs_id,&paquete_mando,sizeof(nipc),0);

													if (configuracion.LogActivado == 1)
														log_info(logger, "MAIN", "Se envio la respuesta del pedido de E sobre el sector %d:%d correctamente \n", posicionActualCabezal.cylinder, posicionActualCabezal.sector);
													break;
									}
								}
							}
							break;

						case SIN_INANICION:

							while(TRUE)
							{
								sem_getvalue(&colaPedidos->cantidadPedidos, &sizeCola);

								if (sizeCola > 0)
								{
									//CREO UNA COLA PARA TRABAJAR CON LOS N PEDIDOS
									colaActiva = crearCola();

									pthread_mutex_lock (&mutexCola);

									//ARMO LA COLA DE LOGUEO ANTES DE QUITAR LOS PEDIDOS QUE VOY A PROCESAR
									if(configuracion.LogActivado == 1)
									{
										colaLog = armarColaLogueoSSTF();
									}

									//ARMO LA COLA CON LA QUE VOY A TRABAJAR
									colaActiva = armarColaActiva();

									//ACTUALIZO EL SIZE DE LA COLA
									sem_getvalue(&colaPedidos->cantidadPedidos, &sizeCola);

									pthread_mutex_unlock(&mutexCola);

									//MIENTRAS HAYA PEDIDOS EN LA COLA ACTIVA
									while(colaActiva->frente != NULL)
									{
										if(configuracion.LogActivado == 1)
										{
											colaLog = armarColaLogueoNSTEP();
										}

										pedidoPlanificacion = quitarPedido_NSTEP();

										switch(pedidoPlanificacion.conexion_pfs_id)
										{
											case PEDIDO_CONSOLA:
												paquete_mando = procesoPedidoElegido(pedidoPlanificacion, colaLog);
												//free(colaLogueo);
												switch (pedidoPlanificacion.lectura_o_escritura)
												{
													//ES UN PEDIDO DE TRACE
													case 'L' :
														enviaRespuestaTRACE(posicionActualCabezal, posicionAnterior, pedidoPlanificacion.direccionSector);
														if (configuracion.LogActivado == 1)
															log_info(logger, "MAIN", "Se envio la respuesta del pedido de E sobre el sector %d:%d correctamente \n", posicionActualCabezal.cylinder, posicionActualCabezal.sector);
														break;

														//ES UN PEDIDO DE CLEAN
													case 'E' :
														enviaRespuestaCLEAN(paquete_mando.type);
														if (configuracion.LogActivado == 1)
															log_info(logger, "MAIN", "Se envio la respuesta del pedido de E sobre el sector %d:%d correctamente \n", posicionActualCabezal.cylinder, posicionActualCabezal.sector);
														break;
												}
												break;

												//ES UN PEDIDO DE RAID
													default:
														paquete_mando = procesoPedidoElegido(pedidoPlanificacion, colaLog);
														//free(colaLogueo);
														//LUEGO DE SER PROCESADO, ES DECIR, ACTUALIZADO LA POSICION ACTUAL DEL CABEZAL, SE DEBE BORRAR DE LA LISTA
														send(pedidoPlanificacion.conexion_pfs_id,&paquete_mando,sizeof(nipc),0);
														if (configuracion.LogActivado == 1)
															log_info(logger, "MAIN", "Se envio la respuesta del pedido de E sobre el sector %d:%d correctamente \n", posicionActualCabezal.cylinder, posicionActualCabezal.sector);
														break;
										}
									}

									destruirCola(colaActiva);
								}
							}
							break;
					}

					break;
	}

	destruirCola(colaPedidos);
	close(cliente_ppd);

	return EXIT_SUCCESS;
}
